package com.cpj.aop;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import com.alibaba.fastjson.JSONObject;
import com.cpj.annotation.NeedLogin;
import com.cpj.base.code.ErrorCode;
import com.cpj.base.model.RequestParam;
import com.cpj.exception.BizException;
import com.cpj.exception.ParamException;
import com.cpj.base.model.User;
import com.cpj.threadLocal.RequestThreadLocal;
import com.cpj.threadLocal.UserThreadLocal;
import com.cpj.utils.RedisUtil;
import io.micrometer.core.instrument.util.StringUtils;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;


/**
 * 控制器切面
 *
 * @author lichuang
 */

@Aspect
@Component
@Service
public class ControllerAspect {
    @Autowired
    RedisUtil redisUtil;

    private static final Logger logger = LoggerFactory.getLogger(ControllerAspect.class);

    /**
     * 调用controller包下的任意类的任意方法时均会调用此方法
     */
    @Around("execution(* com.cpj..*.controller.*.*(..))")
    public Object run1(ProceedingJoinPoint joinPoint) throws Throwable {
        //开始计时
        TimeInterval timer = DateUtil.timer();
        Object[] args = joinPoint.getArgs();            //获取方法参数值数组
        Object[] params =args;
        BindingResult bindingResult=null;
        if(args.length>1){
            try {
                bindingResult= (BindingResult) args[args.length-1];
                params=Arrays.copyOf(params, args.length-1);
            }catch (Exception e){}
        }
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();//得到其方法签名

        RequestMapping requestMapping = methodSignature.getMethod().getAnnotation(RequestMapping.class);
        ApiOperation apiOperation = methodSignature.getMethod().getAnnotation(ApiOperation.class);
        logger.info("请求接口:{},请求方式:{},请求接口:{},请求方法:{}",apiOperation.value(),requestMapping.method(),requestMapping.value(), methodSignature.getMethod().getName());
        NeedLogin needLogin = methodSignature.getMethod().getAnnotation(NeedLogin.class);
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        String token="";
        if(request.getCookies()!=null){
            for (Cookie cookie : request.getCookies()) {
                switch(cookie.getName()){
                    case "token":
                        token = cookie.getValue();
                        break;
                    default:
                        break;
                }
            }
        }
        if(StringUtils.isEmpty(token)){
            String cookie= request.getHeader("cookie");
            if(StringUtils.isNotEmpty(cookie)){
                String[] split = cookie.split(";");
                for(int i=0;i<split.length;i++){
                    if(split[i].contains("token")){
                        token=split[i].split("=")[1];
                    }
                }
            }
        }


        RequestParam requestParam = new RequestParam();

        String ip = request.getHeader("X-Real-IP");
        if (ip == null || "".equals(ip.trim())) {
            ip = request.getRemoteAddr();
        }
        requestParam.setIp(ip);
        if(StringUtils.isNotEmpty(token)){
            requestParam.setToken(token);
            UserThreadLocal.set(JSONObject.parseObject(redisUtil.getUser(token), User.class));
        }
        RequestThreadLocal.set(requestParam);
        if(needLogin!=null){
            if(!redisUtil.hasKey(token)){
                throw new BizException(ErrorCode.UN_LOGIN,"授权失效,请重新登录");
            }
        }
        if (bindingResult!=null && bindingResult.hasErrors()){
            logger.info("响应时间{}毫秒,缺少参数",timer.interval());
            throw new ParamException(bindingResult.getFieldError().getDefaultMessage());
        }
        //动态修改其参数
        //注意，如果调用joinPoint.proceed()方法，则修改的参数值不会生效，必须调用joinPoint.proceed(Object[] args)
        Object result = joinPoint.proceed(args);
        logger.info("响应时间{}毫秒,响应结果为{}",timer.interval(), JSONObject.toJSONString(result));
        //如果这里不返回result，则目标对象实际返回值会被置为null
        timer.intervalRestart();//返回花费时间，并重置开始时间
        return result;
    }
}